package p3.game;

import java.util.Random;

import p3.structures.Tree;
import p3.structures.TreeNode.Function;
import p3.structures.TreeNode.Terminal;

public class GameBoard {

	public final int SIZE = 20;
	
	protected Position defender, rocket, alien;
	
	protected boolean rocketInBoard; 
	protected boolean finish;
	protected int time;
	protected int distance;
	
	
	public GameBoard(){
		defender = new Position();
		rocket = new Position();
		alien = new Position();
		
		rocketInBoard = false;
		finish = false;
		time = 0;
		distance = 0;
	}
	
	public GameBoard(GameBoard gb) {
		defender = new Position(gb.defender);
		rocket = new Position(gb.rocket);
		alien = new Position(gb.alien);
		
		rocketInBoard = gb.rocketInBoard;
		finish = gb.finish;
		time = gb.time;
		distance = gb.distance;
	}
	
	/**
	 * Initialize boardGame with defender on a random position on last row and alien on a random
	 * position between six first rows 
	 */
	public void randomInit(){
		Random r = new Random();
		defender.x = r.nextInt(SIZE-1);
		defender.y = SIZE-1;		
		alien.x = r.nextInt(SIZE-1);
		alien.y = r.nextInt(6);		
		alien.dir = r.nextBoolean() ? Direction.EAST : Direction.WEST;
	}
	
	/**
	 * Executes an instruction given by tree and returns the score of the game
	 * @param tree
	 * @return
	 */
	public int execute(Tree tree){
		if (finish) return 0;
		int ret = 0;
		if (tree.isLeaf()){
			if (tree.getTerm() == Terminal.FIRE){
				if (!rocketInBoard){
					rocketInBoard = true;
					rocket.x = defender.x;
					rocket.y = defender.y;
					time++;
					finish = step();
					ret = 0;
				}
			} else if (tree.getTerm() == Terminal.LEFT){
				if (defender.x != 0) defender.x--;
				time++;
				finish = step();
				ret = 0;
			} else if (tree.getTerm() == Terminal.RIGHT){
				if (defender.x != SIZE-1) defender.x++;
				time++;
				finish = step();
				ret = 0;
			} else if (tree.getTerm() == Terminal.DIST_X){
				int dist = defender.x - alien.x;
				if (alien.dir == Direction.EAST) return dist;
				else if (alien.dir == Direction.WEST) return -dist;				
			} else if (tree.getTerm() == Terminal.DIST_Y){
				ret = defender.y - alien.y;
			}
		} else {
			if (tree.getTerm() == Function.IF){
				int value1 = execute(tree.getChild(0));
				if (!finish){
					if (value1 != 0) ret =  execute(tree.getChild(1));
					else ret = execute(tree.getChild(2));
				} else {
					ret = value1;
				}
			} else if (tree.getTerm() == Function.PROGN2){
				int value1 = execute(tree.getChild(0));
				if (!finish){
					int value2 = execute(tree.getChild(1));
					ret = value1 + value2;
				}
			} else if (tree.getTerm() == Function.PROGN3){
				int value1 = execute(tree.getChild(0));
				if (!finish){
					int value2 = execute(tree.getChild(1));
					if (!finish){
						int value3 = execute(tree.getChild(2));
						ret = value1 + value2+ value3;
					}
				}
			} else if (tree.getTerm() == Function.EQ){
				int value1 = execute(tree.getChild(0));
				int value2 = execute(tree.getChild(1));
				ret = (value1 == value2) ? 1 : 0;
			}
		}
		return ret;
	}
	
	
	public int execute(Tree tree, BoardPanel boardPanel) {
		if (finish) return 0;
		int ret = 0;
		if (tree.isLeaf()){
			if (tree.getTerm() == Terminal.FIRE){
				if (!rocketInBoard){
					rocketInBoard = true;
					rocket.x = defender.x;
					rocket.y = defender.y;
					time++;
					finish = step(boardPanel);
					ret = 0;
				}
			} else if (tree.getTerm() == Terminal.LEFT){
				if (defender.x != 0) defender.x--;
				time++;
				finish = step(boardPanel);
				ret = 0;
			} else if (tree.getTerm() == Terminal.RIGHT){
				if (defender.x != SIZE-1) defender.x++;
				time++;
				finish = step(boardPanel);
				ret = 0;
			} else if (tree.getTerm() == Terminal.DIST_X){
				int dist = defender.x - alien.x;
				if (alien.dir == Direction.EAST) return dist;
				else if (alien.dir == Direction.WEST) return -dist;				
			} else if (tree.getTerm() == Terminal.DIST_Y){
				ret = defender.y - alien.y;
			}
		} else {
			if (tree.getTerm() == Function.IF){
				int value1 = execute(tree.getChild(0), boardPanel);
				if (!finish){
					if (value1 != 0) ret =  execute(tree.getChild(1), boardPanel);
					else ret = execute(tree.getChild(2), boardPanel);
				} else {
					ret = value1;
				}
			} else if (tree.getTerm() == Function.PROGN2){
				int value1 = execute(tree.getChild(0), boardPanel);
				if (!finish){
					int value2 = execute(tree.getChild(1), boardPanel);
					ret = value1 + value2;
				}
			} else if (tree.getTerm() == Function.PROGN3){
				int value1 = execute(tree.getChild(0), boardPanel);
				if (!finish){
					int value2 = execute(tree.getChild(1), boardPanel);
					if (!finish){
						int value3 = execute(tree.getChild(2), boardPanel);
						ret = value1 + value2+ value3;
					}
				}
			} else if (tree.getTerm() == Function.EQ){
				int value1 = execute(tree.getChild(0), boardPanel);
				int value2 = execute(tree.getChild(1), boardPanel);
				ret = (value1 == value2) ? 1 : 0;
			}
		}
		return ret;		
	}

	public boolean step(){
		
		if (alien.dir == Direction.EAST){
			alien.x++;
			if (alien.x > SIZE-1){
				alien.x = SIZE-1;
				alien.y++;
				alien.dir = Direction.WEST;
			}
		} else if (alien.dir == Direction.WEST){
			alien.x--;
			if (alien.x < 0){
				alien.x = 0;
				alien.y++;
				alien.dir = Direction.EAST;
			}
		}
		
		if (alien.y == SIZE-1) finish = true;
		
		if (rocketInBoard){
			rocket.y--;
			if (rocket.y < 0) rocketInBoard = false;
			if (rocket.y == alien.y){
				if (rocket.x == alien.x ) finish = true;
				else distance += Math.abs(rocket.x - alien.x);
			}
			
		}
		return finish;
	}
	
public boolean step(BoardPanel boardPanel){
		
		if (alien.dir == Direction.EAST){
			alien.x++;
			if (alien.x > SIZE-1){
				alien.x = SIZE-1;
				alien.y++;
				alien.dir = Direction.WEST;
			}
		} else if (alien.dir == Direction.WEST){
			alien.x--;
			if (alien.x < 0){
				alien.x = 0;
				alien.y++;
				alien.dir = Direction.EAST;
			}
		}
		
		if (alien.y == SIZE-1) finish = true;
		
		if (rocketInBoard){
			rocket.y--;
			if (rocket.y < 0) rocketInBoard = false;
			if (rocket.y == alien.y){
				if (rocket.x == alien.x ) finish = true;
				else distance += Math.abs(rocket.x - alien.x);
			}
			
		}
		
		boardPanel.refresh(boardPanel.refreshTime);		
		return finish;
	}
	
	public int getTime(){return time;}
	public int getDistance(){return distance;}
	public boolean isFinished(){return finish;}	
	public boolean userLose() {return alien.y == SIZE-1;}
	
	public void resetTime() { time = 0;}
	
	
	public GameBoard getCopy() { return new GameBoard(this); }
	
		
	public enum Direction{EAST,WEST}
	public class Position {
		public int x;
		public int y;
		public Direction dir;
		
		public Position(){ x = -1; y = -1; dir = Direction.WEST; }
		public Position(Position pos){ x = pos.x; y = pos.y; dir = pos.dir; }
	}
}
